
package com.sausageking.client.consumer.ui;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.SpanElement;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.sausageking.shared.Checkin;
import com.sausageking.shared.Offer;
import com.sausageking.shared.Offer.TRIGGER;
import com.sausageking.shared.OfferUtil;

public class DealViewImpl extends Composite implements DealView {

  interface DealWidgetUiBinder extends UiBinder<Widget, DealViewImpl> {
  }

  private static DealWidgetUiBinder uiBinder = GWT
      .create(DealWidgetUiBinder.class);

  private Offer offer;

  private Presenter presenter;

  @UiField
  HTMLPanel stampablePanel;

  @UiField
  HTMLPanel redeemingPanel;

  @UiField
  HTMLPanel redeemablePanel;

  @UiField
  Label description1;

  @UiField
  Label description2;

  @UiField
  Label finePrint;

  @UiField
  Image offerImage;

  @UiField
  SpanElement confirmationCode;

  @UiField
  Label numCheckinLabel;

  private Timer timer;

  private Date expireTime;

  @UiField
  SpanElement expirationTime;

  @UiField
  VerticalPanel stampPanel;

  @Inject
  public DealViewImpl(@Assisted Presenter presenter) {
    initWidget(uiBinder.createAndBindUi(this));
    this.presenter = presenter;
  }

  @Override
  public DealView setRedemptionCode(String redemptionCode) {
    this.confirmationCode.setInnerText(redemptionCode);
    return this;
  }

  @Override
  public DealViewImpl setRedemptionExpires(long msToExpire) {
    setFormattedExpirationTime(msToExpire);
    setTimer(msToExpire);
    return this;
  }

  private void setTimer(long msToExpire) { // TODO: move to presenter
    expireTime = new Date(new Date().getTime() + msToExpire);
    if (timer != null) {
      timer.cancel();
    }
    timer = new Timer() {

      @Override
      public void run() {
        long timeToExpire = expireTime.getTime() - new Date().getTime();
        if (timeToExpire < 0) {
          timeToExpire = 0;
          timer.cancel();
        }
        setFormattedExpirationTime(timeToExpire);
      }
    };
    timer.schedule(1000);
    timer.scheduleRepeating(1000);
  }

  private DealView setFormattedExpirationTime(long msToExpire) {
    long expInSecond = msToExpire / 1000;
    long minutes = expInSecond / 60;
    String minuteStr = minutes > 1 ? "minutes" : "minute";
    long seconds = expInSecond % 60;
    String secondStr = seconds > 1 ? "seconds" : "second";
    String formattedStr = new StringBuffer()
        .append(NumberFormat.getFormat(" 0 ").format(minutes))
        .append(minuteStr)
        .append(NumberFormat.getFormat(" 0 ").format(seconds))
        .append(secondStr).toString();
    this.expirationTime.setInnerText(formattedStr);
    return this;
  }

  @Override
  public DealView setOffer(Offer offer) {
    this.offer = offer;
    if (offer.getTrigger().equals(TRIGGER.EVERY_X_CHECK_INS)) {
      description2.setText(new OfferUtil().getRewardStr(offer));
      description1.setText("Buy " + offer.getCheckinCount() + " get 1");
      StringBuffer sb = new StringBuffer();
      sb.append("*")
          .append(offer.getCheckinCount())
          .append(" stamps are required to redeem ")
          .append(offer.getRewardDescription())
          .append(". Limit one stamp per day.");
      finePrint.setText(sb.toString());
    }
    offerImage.setUrl(offer.getImageUrl());
    return this;
  }

  @Override
  public DealView showStamps(List<Checkin> checkins, boolean redeeming,
      boolean redeemable) {
    StampPanel sp = new StampPanel(stampPanel, offer.getCheckinCount());

    if (!offer.getTrigger().equals(TRIGGER.EVERY_X_CHECK_INS)) {
      return this;
    }

    if (redeeming || redeemable) {
      for (int i = 0; i < offer.getCheckinCount(); i++) {
        sp.add(true);
      }
      return this;
    }

    long numCheckins = checkins == null ? 0 : checkins.size();

    long numCheckinsToNextReward = offer.getCheckinCount()
        - (numCheckins - numCheckins / offer.getCheckinCount()
            * offer.getCheckinCount());

    for (int i = 0; i < offer.getCheckinCount(); i++) {
      sp.add(i < offer.getCheckinCount() - numCheckinsToNextReward);
    }
    return this;
  }

  @UiHandler("stampButton")
  void onStampClick(ClickEvent e) {
    presenter.startStampDialog();
  }

  @UiHandler("redeemButton")
  void onRedeemClick(ClickEvent e) {
    presenter.startRedeemDialog();
  }

  @Override
  public DealView setStampable(boolean stampable) {
    stampablePanel.setVisible(stampable);
    numCheckinLabel.setVisible(stampable);
    return this;
  }

  @Override
  public DealView setRedeemable(boolean redeemable) {
    redeemablePanel.setVisible(redeemable);
    return this;
  }

  @Override
  public DealView setRedeeming(boolean redeeming) {
    redeemingPanel.setVisible(redeeming);
    return this;
  }

  private class StampPanel {

    private List<HorizontalPanel> rows;

    private int nth;

    public StampPanel(VerticalPanel stampPanel, int totalStamps) {
      this.nth = 0;
      rows = new ArrayList<HorizontalPanel>();
      for (int i = 0; i <= totalStamps / 5; i++) {
        HorizontalPanel row = new HorizontalPanel();
        stampPanel.add(row);
        rows.add(row);
      }
    }

    public void add(boolean stamped) {
      Widget stamp = stamped
          ? stampedNumber(nth + 1)
          : unStampedNumber(nth + 1);
      rows.get(nth / 5).add(stamp);
      nth++;
    }

    private Widget unStampedNumber(int num) {
      Label label = new Label(String.valueOf(num));
      label.addStyleName("unstamped");
      return label;
    }

    private Widget stampedNumber(int num) {
      Label label = new Label(String.valueOf(num));
      label.addStyleName("stamped");
      return label;
    }
  }
}
